#include "../include/mytools.h"
namespace zzyserver{

	class FServer
	{
	
		std::string rootfilepath; 
		bool hiddenfile = false;
		long outfilesize = 0;
		MyTools tools;
		//int hassend ;
	public:
		bool sendfile(int new_fd, std::string path, std::string file_name, char *buffer)
		{
			
			printf("fd = %d, file: %s begin send...\n", new_fd, path.c_str());
			char data_tmp[512] = {0}; //要发的数据部分内容
			long file_size = tools.get_file_size(path);
			if (-1 == file_size)
			{
				printf("fd = %d, get_file_size err!\n", new_fd);
				return false;
			}
			sprintf(data_tmp, "<file><name>%s</name><size>%ld</size></file>", file_name.c_str(), file_size); //要发送的数据部分内容

			struct TCP_STRUCT_DATA struct_data; //数据头
			memset(&struct_data, 0x0, sizeof(struct_data));
			struct_data.m_cmd = GET_FILE;			   //获取文件命令
			struct_data.m_data_len = strlen(data_tmp); //数据部分真实的长度

			memset(buffer, 0, BUFFER_SIZE);
			int send_len = sizeof(struct_data);
			memcpy(buffer, &struct_data, send_len);

			//向服务器发送buffer中的数据
			int len = send(new_fd, buffer, send_len, 0); //发数据头部分
			if (len < 0)
			{
				printf("fd = %d, send err...\n", new_fd); //发送失败
				return false;
			}
			memset(buffer, 0, BUFFER_SIZE);
			
			if((len = recv(new_fd, buffer, 6, 0)) < 0)
			{
				printf("fd = %d, recv err...\n", new_fd); //接收失败
				return false;
			}
			if(strncmp(buffer, "#over#", 6))
			{
				printf("fd = %d, recv data is not #over#...\n", new_fd); //接收失败
				return false;
			}
			len = send(new_fd, data_tmp, struct_data.m_data_len, 0);
			if (len < 0)
			{
				printf("fd = %d, send err...\n", new_fd); //发送失败
				return false;
			}

			memset(buffer, 0, BUFFER_SIZE);
			
			if((len = recv(new_fd, buffer, 6, 0)) < 0)
			{
				printf("fd = %d, recv err...\n", new_fd); //接收失败
				return false;
			}
			if(strncmp(buffer, "#over#", 6))
			{
				printf("fd = %d, recv data is not #over#...\n", new_fd); //接收失败
				return false;
			}

			int file2_fp = open(path.c_str(), O_RDONLY);
			if(file2_fp < 0)
			{
				printf("fd = %d, file: %s open failed.\n", new_fd, path.c_str());
				return false;
			}

			int length = 0;
			int hassend = 0;;
			memset(buffer, 0, BUFFER_SIZE);
			// 每读取一段数据，便将其发送给客户端，循环直到文件读完为止
			while ((length = read(file2_fp, buffer, BUFFER_SIZE)) > 0)
			{
				if (send(new_fd, buffer, length, MSG_NOSIGNAL) < 0)
				{
					printf("fd = %d, file: %s send failed.\n", new_fd, path.c_str());
					close(file2_fp);
					return false;
				}
				hassend += length;
				if(hassend > MAX_SENDLENGTH)
				{
					memset(buffer, 0, BUFFER_SIZE);
					if (recv(new_fd, buffer, BUFFER_SIZE, 0) < 0)
					{
						printf("fd = %d, file: %s recv failed.\n", new_fd, path.c_str());
						close(file2_fp);
						return false;
					}
					if(strncmp(buffer, "#beginsend#", 11))
					{
						printf("fd = %d, file: %s #beginsend# recv failed.\n", new_fd, path.c_str());
						close(file2_fp);
						return false;
					}
					hassend = 0;
				}
				memset(buffer, 0, BUFFER_SIZE);
			}
			
			memset(buffer, 0, BUFFER_SIZE);
			if (recv(new_fd, buffer, BUFFER_SIZE, 0) < 0)
			{
				printf("fd = %d, file: %s recv failed.\n", new_fd, path.c_str());
				close(file2_fp);
				return false;
			}
			if(strncmp(buffer, "#successful#", 12))
			{
				printf("fd = %d, file: %s #successful# recv failed.\n", new_fd, path.c_str());
				close(file2_fp);
				return false;
			}
			// 关闭文件
			close(file2_fp);
			printf("fd = %d, file: %s send successful.\n", new_fd, path.c_str());
			return true;
		}

		bool senddir(int new_fd, std::string path, std::string file_name, char *buffer)
		{
			char temp_data[100];
			printf("fd = %d, dir: %s begin send...\n", new_fd, path.c_str());
			bzero(temp_data, sizeof temp_data);
			sprintf(temp_data, "<file><name>%s</name><size>%ld</size></file>", file_name.c_str(), (long)1); //要发送的数据部分内容

			struct TCP_STRUCT_DATA struct_data; //数据头
			memset(&struct_data, 0x0, sizeof(struct_data));
			struct_data.m_cmd = GET_DIR;			   //获取文件命令
			struct_data.m_data_len = strlen(temp_data); //数据部分真实的长度

			memset(buffer, 0, BUFFER_SIZE);
			int send_len = sizeof(struct_data);
			memcpy(buffer, &struct_data, send_len);

			//向服务器发送buffer中的数据
			int len = send(new_fd, buffer, send_len, 0); //发数据头部分
			if (len < 0)
			{
				printf("fd = %d, send err...\n", new_fd); //发送失败
				return false;
			}
			memset(buffer, 0, BUFFER_SIZE);
			if((len = recv(new_fd, buffer, 6, 0)) < 0)
			{
				printf("fd = %d, recv err...\n", new_fd); //接收失败
				return false;
			}
			if(strncmp(buffer, "#over#", 6))
			{
				printf("fd = %d, recv data is not #over#...\n", new_fd); //接收失败
				return false;
			}
			len = send(new_fd, temp_data, struct_data.m_data_len, 0);
			if (len < 0)
			{
				printf("fd = %d, send err...\n", new_fd); //发送失败
				return false;
			}
			memset(buffer, 0, BUFFER_SIZE);
			if((len = recv(new_fd, buffer, 6, 0)) < 0)
			{
				printf("fd = %d, recv err...\n", new_fd); //接收失败
				return false;
			}
			if(strncmp(buffer, "#over#", 6))
			{
				printf("fd = %d, recv data is not #over#...\n", new_fd); //接收失败
				return false;
			}
			//定义一个目录流指针
			DIR *p_dir = NULL;
		
			//定义一个目录结构体指针
			struct dirent *p_dirent = NULL;
		
			//打开目录，返回一个目录流指针指向第一个目录项
			p_dir = opendir(path.c_str());
			if(p_dir == NULL)
			{
				printf("opendir error\n");
				return false;
			}
			bool sendflag = true;
			//循环读取每个目录项, 当返回NULL时读取完毕
			while((p_dirent = readdir(p_dir)) != NULL)
			{	
				if(!strcmp(p_dirent->d_name, ".") || !strcmp(p_dirent->d_name, ".."))
					continue;
				if(!hiddenfile)
				{
					if(p_dirent->d_name[0] == '.')
						continue;
				}
				//备份之前的目录名
				std::string backupDirName;
				//如果目录项仍是一个目录的话，进入目录

				//准备下一级
				
				backupDirName = path;
				if(backupDirName[backupDirName.size()-1] == '\0') backupDirName[backupDirName.size()-1] = '/';
				else backupDirName += '/';
				backupDirName += p_dirent->d_name;
				if(p_dirent->d_type == DT_DIR)
					sendflag = senddir(new_fd, backupDirName, p_dirent->d_name, buffer);//递归调用
				else
					sendflag = sendfile(new_fd, backupDirName, p_dirent->d_name, buffer);
					
				if(!sendflag)
				{
					printf("failed to send %s.\n", path.c_str());
					return false;
				}
			}
			
			if (send(new_fd, "#enddir#", 8, MSG_NOSIGNAL) < 0)
			{
				printf("fd = %d, dir: %s send failed.\n", new_fd, path.c_str());
				return false;
			}
			closedir(p_dir);
			memset(buffer, 0, BUFFER_SIZE);
			if (recv(new_fd, buffer, BUFFER_SIZE, 0) < 0)
			{
				printf("fd = %d, dir: %s recv failed.\n", new_fd, path.c_str());
				return false;
			}
			if(strncmp(buffer, "#dirsuccessful#", 15))
			{
				printf("fd = %d, dir: %s #successful# recv failed.\n", new_fd, path.c_str());
				return false;
			}
			printf("fd = %d, dir: %s send successful.\n", new_fd, path.c_str());
			return sendflag;
		}

		bool sendfilepath(int new_fd, std::string path, int level, char *buffer, int &hassend)
		{
			
			//定义一个目录流指针
			DIR *p_dir = NULL;
			//定义一个目录结构体指针
			struct dirent *p_dirent = NULL;
		
			//打开目录，返回一个目录流指针指向第一个目录项
			p_dir = opendir(path.c_str());
			if(p_dir == NULL)
			{
				sprintf(buffer, "opendir: %s error\n", path.c_str());
				if(send(new_fd, buffer, strlen(buffer), MSG_NOSIGNAL) < 0)
				{
					printf("fd: %d send error", new_fd);
				}
				return false;
			}
			
			//循环读取每个目录, 当返回NULL时读取完
			while((p_dirent = readdir(p_dir)) != NULL)
			{
				//备份之前的目录名
				std::string backupDirName;
				
				if(!strcmp(p_dirent->d_name, ".") || !strcmp(p_dirent->d_name, ".."))
					continue;
				if(!hiddenfile)
				{
					if(p_dirent->d_name[0] == '.')
						continue;
				}
				int i = 0, j = 0;
				memset(buffer, 0, BUFFER_SIZE);
				int istep = 0;
				for(; i < level; i++, j++)
				{
					if(j > 200)
					{
						hassend += strlen(buffer);
						if(send(new_fd, buffer, strlen(buffer), MSG_NOSIGNAL) < 0)
						{
							printf("fd = %d send error.\n", new_fd);
							return false;
						}
						istep = i;
						j = 0;
					}
					sprintf(buffer + 5* (i - istep), "    |");	
				}
				hassend += strlen(buffer);
				if(send(new_fd, buffer, strlen(buffer), MSG_NOSIGNAL) < 0)
				{
					printf("fd = %d send error.\n", new_fd);
					return false;
				}
				memset(buffer, 0, BUFFER_SIZE);
				sprintf(buffer,"    %s\n", p_dirent->d_name);
				//printf("%s", buffer);
				if(send(new_fd, buffer, strlen(buffer), MSG_NOSIGNAL) < 0)
				{
					printf("fd = %d send error.\n", new_fd);
					return false;
				}
				hassend += strlen(buffer);
				if(hassend > MAX_SENDLENGTH)
				{
					memset(buffer, 0, BUFFER_SIZE);
					if (recv(new_fd, buffer, BUFFER_SIZE, 0) < 0)
					{
						printf("fd = %d, file: %s recv failed.\n", new_fd, path.c_str());
						return false;
					}
					if(strncmp(buffer, "#beginsend#", 11))
					{
						printf("fd = %d, file: %s #beginsend# recv failed.\n", new_fd, path.c_str());
						return false;
					}
					hassend = 0;
				}
				//如果目录项仍是一个目录的话，进入目录
				if(p_dirent->d_type == DT_DIR)
				{
					backupDirName = path;
					if(backupDirName[backupDirName.size()-1] == '\0') backupDirName[backupDirName.size()-1] = '/';
					else backupDirName += '/'; 
					backupDirName += p_dirent->d_name;
					if(!sendfilepath(new_fd, backupDirName, level + 1, buffer, hassend))return false;//递归调用
				}
			}
			
			closedir(p_dir);
			return true;
		}


		bool net_thread(int fd)
		{
			//pthread_detach(pthread_self()); //线程分离
			int &new_fd  = fd;
			int file_fp;
			
			// recv函数接收数据到缓冲区buffer中
			char buffer[BUFFER_SIZE];
			memset(buffer, 0, BUFFER_SIZE);
			if (recv(new_fd, buffer, BUFFER_SIZE, 0) < 0)
			{
				printf("fd = %d, outfilesize recv failed.\n", new_fd);
				close(new_fd);
				return false;
			}
			if(!strncmp(buffer, "#beginpath#", 11))
			{
				struct TCP_STRUCT_DATA struct_data; //数据头
				memset(&struct_data, 0x0, sizeof(struct_data));
				struct_data.m_cmd = GET_DIR;			   //获取文件命令
				struct_data.m_data_len = outfilesize; //数据部分真实的长度

				memset(buffer, 0, BUFFER_SIZE);
				int send_len = sizeof(struct_data);
				memcpy(buffer, &struct_data, send_len);
				//printf("file length: %ld\n", outfilesize);
				//向客户端发送buffer中的数据
				int len = send(new_fd, buffer, send_len, 0); //发数据头部分
				if (len < 0)
				{
					printf("fd = %d, send err...\n", new_fd); //发送失败
					close(new_fd);
					return false;
				}
				memset(buffer, 0, BUFFER_SIZE);
				if (recv(new_fd, buffer, BUFFER_SIZE, 0) < 0)
				{
					printf("fd = %d, outfilesize recv failed.\n", new_fd);
					close(new_fd);
					return false;
				}
				if(strncmp(buffer, "#over#", 6))
				{
					printf("fd = %d, #over# recv failed.\n", new_fd);
					close(new_fd);
					return false;
				}
				int hassend = 0;
				if(!sendfilepath(new_fd, rootfilepath, 0, buffer, hassend))
				{
					printf("fd: %d server recv filepath failed.\n", new_fd);
					close(new_fd);
					return false;
				}
				memset(buffer, 0, BUFFER_SIZE);
				if (recv(new_fd, buffer, BUFFER_SIZE, 0) < 0)
				{
					printf("fd = %d, outfilesize recv failed.\n", new_fd);
					close(new_fd);
					return false;
				}
				if(strncmp(buffer, "#pathover#", 10))
				{
					printf("fd = %d, #pathover# recv failed.\n", new_fd);
					close(new_fd);
					return false;
				}
				close(new_fd);
				return true;
			}
			
			if(strlen(buffer) == 0)
			{
				close(new_fd);
				printf("fd = %d client has disconnect.\n", new_fd);
				close(new_fd);
				return false;
			}
			// 然后从buffer(缓冲区)拷贝到file_name中
			std::string file_name = buffer;
			memset(buffer, 0, BUFFER_SIZE);
			if(!tools.judgepath(file_name))
			{
				if (send(new_fd, "#file not found#", 16, MSG_NOSIGNAL) == -1)
				{
					printf("fd = %d send error.\n", new_fd);
					close(new_fd);
					return false;
				}
			}
			// 打开文件并读取文件数据
			std::string sendfile_path = rootfilepath;
			if(sendfile_path.size() > 0 && sendfile_path[sendfile_path.size()-1] == '\0')sendfile_path[sendfile_path.size()-1] =  '/';
			else sendfile_path += '/';
			sendfile_path += file_name;
			int sendtype = GET_FILE;
			file_fp = tools.judgefile(sendfile_path);
			if(file_name.size() > 1 && file_name[0] == '.' && !hiddenfile)file_fp = -1;
			
			if (file_fp < 0)
			{
				if (send(new_fd, "#file not found#", 16, MSG_NOSIGNAL) == -1)
				{
					printf("fd = %d send error.\n", new_fd);
					close(new_fd);
					return false;
				}
			}
			else
			{
				bool recvflag = true;
				switch(file_fp)
				{
					case 1:sendtype = GET_FILE;break;
					case 2:sendtype = GET_DIR;break;
					default:break;
				}
				if(sendtype == GET_DIR)
				{
					recvflag = senddir(new_fd, sendfile_path.c_str(), file_name.c_str(), buffer);
				}
				else recvflag = sendfile(new_fd, sendfile_path.c_str(), file_name.c_str(), buffer);
				if(!recvflag)
					printf("fd = %d, file or dir: %s send failed!\n", new_fd, file_name.c_str());
			}

			close(new_fd);
			printf("fd = %d, has disconnect.\n", new_fd);
			return true;
		}

		void server(int server_fd, Popt opt)
		{
			int n = 1;
			printf("server begin!\n");
			printf("server listening at %s:%d\n", opt->ip.c_str(), opt->port);
			printf("file lists:\n");
			outfilesize = tools.TraverseDir(rootfilepath, 0, opt->hidden);
			// if(daemon(1, 0) == -1)
			// {
			// 	perror("daemon: ");
			// 	exit(EXIT_FAILURE);
			// }
			while(1)
			{
				struct sockaddr_in client_addr;
				socklen_t size = sizeof(client_addr);
				int new_fd = accept(server_fd, (struct sockaddr *)&client_addr, &size);  //server_fd服务器的socket描述字,&client_addr指向struct sockaddr *的指针,&size指向协议地址长度指针
		
				if(-1 == new_fd)
				{
					printf("accept error.\n");
					printf("this is %d accept , and new_fd = %d\n", n++, new_fd);
					continue;       //进行下一次循环
				}
				//inet_ntoa将一个十进制网络字节序转换为点分十进制IP格式的字符串。
				printf("accept client ip: %s:%d\n",inet_ntoa(client_addr.sin_addr),client_addr.sin_port);
				auto myfunc = [&](int new_fd){return net_thread(new_fd);}; 
				std::thread mythread(myfunc, new_fd);
				mythread.detach();
			}
			printf("server stop!\n");
			close(server_fd);

		}

		int start(int argc, char *argv[])
		{
			//解析参数
			Opt opt;
			memset(&opt , 0, sizeof(Opt));
			opt.show = true;
			opt.ip = "localhost";
			opt.port = SERVER_PORT;
			struct option long_options[] = 
			{
					{"listen", required_argument, 0, 'l'},
					{"hidden", no_argument, 0, 'H'},
					{"port", required_argument, 0, 'p'},
					{"help", no_argument, 0, 'h'},
					{0, 0, 0, 0}
			};
			char shortopts[] = "l:Hp:h";
			if(!tools.parseopt(argc, argv, &opt, long_options, shortopts, true))
				return 0;
			
			//printf("argc = %d\n",argc);
			if(tools.judgefile(argv[argc-1]) != 2)
			{
				printf("please set a dir for server.\n");
				return 0;
			}
			rootfilepath =  argv[argc-1];
			int lens = rootfilepath.size();
			if(lens > 1 && rootfilepath[lens-1] == '/')
				rootfilepath[lens-1] = '\0';
			hiddenfile = opt.hidden;
			//初始化套接字
			int server_fd = socket(AF_INET,SOCK_STREAM, 0);
			if(-1 == server_fd)
			{
				perror("socket");
				printf("socket fd has been used up.\n");
				exit(1);
			}
			//绑定端口和ip;
			struct hostent *ht;
			ht = gethostbyname(opt.ip.c_str());
			if(NULL == ht)
			{
				printf("failed to start fserver: %s is not a valid address", opt.ip.c_str());
				exit(2);
			}
			struct sockaddr_in server_addr;   //struct sockaddr_in为结构体类型 ，server_addr为定义的结构体   
			server_addr.sin_family=AF_INET;   //Internet地址族=AF_INET(IPv4协议) 
			server_addr.sin_port=htons(opt.port);  //将主机字节序转化为网络字节序 ,portnum是端口号
			server_addr.sin_addr = *((struct in_addr *)ht->h_addr_list[0]);//IP地址
			if(-1 == bind(server_fd, (struct sockaddr *)&server_addr,sizeof(server_addr)))  //套接字与端口绑定
			{
				perror("bind");
				printf("failed to start fserver: %s:%d already in use\n", opt.ip.c_str(), opt.port);
				exit(3);
			}
			//开启监听
			if(-1==listen(server_fd, MAX_CONNETCION_COUNT)) //5是最大连接数，指服务器最多连接5个用户
			{
				perror("listen");
				exit(4);
			}
			
			server(server_fd, &opt);
			return 0;
		
		}
	};
}

int main(int argc, char *argv[])
{
	zzyserver::FServer fserver;
	fserver.start(argc, argv);
	return 0;
}
 
